package proxy;

import java.util.Arrays;
import java.util.Comparator;
import java.util.Random;

import simulation.SimulatorConfig.ScenarioType;
import simulation.ZipfGenerator;

/**
 * i was lazy in implementing this class ... in the search scenario the content
 * represents a tag belonging to a unit a tag is a hierarchy of topics: e.g.
 * university->informatics->U01
 * 
 * @author Anita
 * 
 */
public class Content implements Cloneable, Comparable<Content>,
		Comparator<Content> {

	/**
	 * The array content simulates a vector of different content components. For
	 * example content[0] could refer to a news component, content[1] to
	 * entertainment component and so on. It is used to simulate similarity of
	 * video units.
	 */
	public int[] content;
	private Random rand;
	public static final int maxTagsPerHierarchy = 10;

	public Content(int contentTypes, Random rand) {
		this.rand = rand;
		content = new int[contentTypes];
	}

	public Content(int contentTypes) {
		this(contentTypes, null);
	}

	public Content(int[] contentVector) {
		content = contentVector.clone();
	}

	/**
	 * Fill tags with uniform distribution or zipf (searchScenario)
	 * 
	 * @param scenario
	 */
	public void fill(ScenarioType scenario) {
		int sum = 0;
		for (int i = 0; i < content.length; i++) {

			// alpha: 1 (highly skewed)
			// n number of possible values
			// returns a number between 1 and n
			// or -1 if didn't work!
			if (scenario == ScenarioType.SEARCH
					|| scenario == ScenarioType.SIMPLEHORMONESEARCH) {
				do {
					content[i] = ZipfGenerator.nextZipf(1,
							(maxTagsPerHierarchy * (i + 1)), rand);
				} while (content[i] < 0);
			} else {
				content[i] = rand.nextInt(100);
				sum += content[i];
			}
		}
		// normalize content vector to average length of 100
		if (scenario == ScenarioType.REF
				|| scenario == ScenarioType.SIMPLEHORMONE) {
			if (sum == 0)
				sum++;
			for (int i = 0; i < content.length; i++)
				content[i] = content[i] * 100 / sum;
		}

	}

	/**
	 * Euclidean distance between 2 contentArrays
	 * 
	 * @param likesContent
	 *            the initial contentArray to compare with
	 */
	public double calculateDistance(int[] likesContent) {
		double dist = 0;
		for (int i = 0; i < likesContent.length; i++)
			dist += Math.pow(likesContent[i] - content[i], 2);
		// scale the distance by the popularity
		return dist;
	}

	/**
	 * Calculates the number of equal hierarchical tags eg.
	 * university.compSc.test vs. university.history.test2 = 1
	 * 
	 * @param toCompareWith
	 */
	public int calculateMatchingTags(int[] toCompareWith) {
		int i;
		for (i = 0; i < content.length; i++) {
			if (content[i] != toCompareWith[i]) {
				break;
			}
		}
		return i;
	}

	/**
	 * calculates the largest common subtree;
	 * 
	 * @param toCompareWith
	 * @return
	 */
	public double calculateTreeDistance(int[] toCompareWith) {
		return content.length + toCompareWith.length - 2
				* calculateMatchingTags(toCompareWith);
	}

	// we need to clone the array too
	public Object clone() {
		Content c;
		try {
			c = (Content) super.clone();
			c.content = content.clone();
			return c;
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		return null;
	}

	// sort content hierarchically
	@Override
	public int compareTo(Content o) {
		for (int i = 0; i < content.length; i++) {
			if (content[i] != o.content[i]) {
				return content[i] - o.content[i];
			}
		}
		return 0;
	}

	@Override
	public String toString() {
		String res = "[";
		String delim = "";
		for (int i : content) {
			res += delim + i;
			delim = ",";
		}
		return res + "]";
	}

	@Override
	public boolean equals(Object obj) {
		Content toCompare = (Content) obj;
		boolean  res = content.hashCode() == toCompare.content.hashCode();
		return res;
		// return Arrays.equals(content, toCompare.content);
	}

	@Override
	public int compare(Content o1, Content o2) {
		return o1.compareTo(o2);
	}

	@Override
	public int hashCode() {
		return Arrays.hashCode(content);
	}
}
